# Copyright (c) 2022 Majjcom
from .bdnet import server
import threading
import socket
import time


class _ServerListener(threading.Thread):
    def __init__(self, serverObj, pairPool, statePipe):
        super().__init__()
        self._s = serverObj
        self._pairPool = pairPool
        self._statePipe = statePipe

    def run(self):
        while self._statePipe[0]:
            try:
                c = self._s.listen()
                get = c.recvMsg()
                clientInfo = {'id': get['id'], 'type': get['type'], 'obj': c}
                self._pairPool.append(clientInfo)
            except socket.timeout:
                pass


class _ServerPairChecker(threading.Thread):
    def __init__(self, connPool, pairPool, statePipe, recvCallback, connCallback):
        super().__init__()
        self._connPool = connPool
        self._pairPool = pairPool
        self._statePipe = statePipe
        self._callback = recvCallback
        self._connCallback = connCallback

    def run(self):
        while self._statePipe[0]:
            for i in self._pairPool:
                for j in self._pairPool:
                    if i['id'] == j['id'] and i['type'] == 'sender' and j['type'] == 'recver':
                        self._pairPool.pop(self._pairPool.index(i))
                        self._pairPool.pop(self._pairPool.index(j))
                        conn = Connection({'sender': i, 'recver': j}, self._callback, self._statePipe)
                        self._connPool.append(conn)
                        if self._connCallback:
                            self._connCallback(conn)
            time.sleep(0.5)


class _ConnectionCleaner(threading.Thread):
    def __init__(self, connPool, statePipe):
        super().__init__()
        self._pool = connPool
        self._pipe = statePipe

    def run(self):
        while self._pipe[0]:
            for i in self._pool:
                if i.isClosed():
                    i.close()
                    self._pool.remove(i)
            time.sleep(5)
        for i in self._pool:
            try:
                i.close()
            except:
                pass
            self._pool.remove(i)


class Server:
    def __init__(self, port):
        self._port = port
        self._state = [True]
        self._connectionPool = list()
        self._connectionPairPool = list()
        self._serverObj = server.Server(port)
        self._serverListener = None
        self._serverPairManager = None
        self._serverCleaner = None

    def startServer(self, recvCallback, connCreatCallback=None):
        if not self._serverListener:
            self._serverListener = _ServerListener(self._serverObj,
                                                   self._connectionPairPool,
                                                   self._state)
            self._serverListener.start()
        if not self._serverPairManager:
            self._serverPairManager = _ServerPairChecker(self._connectionPool,
                                                         self._connectionPairPool,
                                                         self._state,
                                                         recvCallback,
                                                         connCreatCallback)
            self._serverPairManager.start()
        if not self._serverCleaner:
            self._serverCleaner = _ConnectionCleaner(self._connectionPool, self._state)
            self._serverCleaner.start()

    def closeServer(self):
        self._state[0] = False


class _ConnectionRecver(threading.Thread):
    def __init__(self, conn, callback, statePipe, closePipe):
        super().__init__()
        self._c = conn
        self._callback = callback
        self._statePipe = statePipe
        self._closePipe = closePipe

    def run(self):
        while self._statePipe[0] and not self._closePipe[0]:
            try:
                msg = self._c.recvMsg()
                self._callback(msg, self._c)
            except socket.timeout:
                pass


class Connection:
    def __init__(self, conn, callBack, statePipe):
        self._c = conn
        self._callback = callBack
        self._statePipe = statePipe
        self._isCloseed = [False]
        self._recver = _ConnectionRecver(self, self._callback, self._statePipe, self._isCloseed)
        self._recver.start()

    def recvMsg(self):
        try:
            ret = self._c['sender']['obj'].recvMsg()
        except ConnectionError:
            self._isCloseed[0] = True
            ret = None
        return ret

    def sendMsg(self, content: dict):
        try:
            ret = self._c['recver']['obj'].sendMsg(content)
        except ConnectionError:
            self._isCloseed[0] = True
            ret = None
        return ret

    def getId(self):
        return self._c['sender']['id']

    def close(self):
        self._c['sender']['obj'].close()
        self._c['recver']['obj'].close()
        self._isCloseed[0] = True

    def isClosed(self):
        return self._isCloseed[0]
